﻿using DA.Model.Common;
using DA.Model.Plc;
using DA.Utility.Mapper;
using HslCommunication;
using HslCommunication.Core;
using HslCommunication.ModBus;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics.SymbolStore;
using System.Security.AccessControl;
using System.Text;
using System.Threading.Tasks;

namespace DA.Core.Plcs
{
    public class Modbus : IBasePlc
    {
        private ModbusTcpNet busTcpClient = null;
        private IReadWriteNet readWriteNet;
        #region Property
        private byte station = 0x01;
        /// <summary>
        /// 客户端站号
        /// </summary>
        public byte Station
        {
            get { return station; }
            set { station = value; }
        }

        private bool addressStartWithZero = true;
        /// <summary>
        /// 起始地址是否从0开始，默认为true
        /// </summary>
        public bool AddressStartWithZero
        {
            get { return addressStartWithZero; }
            set { addressStartWithZero = value; }
        }

        private bool isCheckMessageId = true;
        /// <summary>
        /// 是否进行检查返回的消息ID和发送的消息ID是否一致，默认为true
        /// </summary>
        public bool IsCheckMessageId
        {
            get { return isCheckMessageId; }
            set { isCheckMessageId = value; }
        }

        private bool isStringReverse = false;
        /// <summary>
        /// 字符串数据是否按照字（2个字节）来反转，默认为false
        /// </summary>
        public bool IsStringReverse
        {
            get { return isStringReverse; }
            set { isStringReverse = false; }
        }

        private ConnectStatus connectStatus;
        /// <summary>
        /// 连接状态
        /// </summary>
        public ConnectStatus ConnectStatus
        {
            get { return connectStatus; }
            set { connectStatus = value; }
        }
        private int interval = 1000;

        public int Interval
        {
            get { return interval; }
            set { interval = value; }
        }

        #endregion

        public void Connect(string ip, int port)
        {
            CheckAddress(ip, port);
            busTcpClient?.ConnectClose();
            busTcpClient = new ModbusTcpNet(ip, port, station);
            busTcpClient.AddressStartWithZero = addressStartWithZero;
            busTcpClient.IsCheckMessageId = isCheckMessageId;
            busTcpClient.IsStringReverse = isStringReverse;

            try
            {
                OperateResult connect = busTcpClient.ConnectServer();
                if (connect.IsSuccess)
                {
                    readWriteNet = busTcpClient;
                    connectStatus = ConnectStatus.Connected;
                }
            }
            catch (Exception ex)
            {
                connectStatus = ConnectStatus.Disconnected;
                throw ex;
            }
        }
        private void CheckAddress(string ip, int port)
        {
            if (string.IsNullOrWhiteSpace(ip))
            {
                throw new ArgumentNullException("ip is null");
            }
        }
        public void Disconnect()
        {
            busTcpClient.ConnectClose();
            connectStatus = ConnectStatus.Disconnected;
        }

        public void Init()
        {

        }

        public async Task<object> ReadAsync(string address, DaDataType dataType, ushort length)
        {
            try
            {
                object content = null;
                var result = await ReadCoreAsync(address, dataType, length);
                if (result.IsSuccess)
                {
                    content = result.Content;
                }
                return content;
                //以下方法是重复读直至成功
                //object content = null;
                //bool readAgain = true;
                //while(readAgain)
                //{
                //    var result = await ReadCore(address, dataType, length);
                //    readAgain = !result.IsSuccess;
                //    content = result.Content;
                //    await Task.Delay(interval);
                //}
                //return content;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public async Task<bool> WriteAsync(string address, DaDataType dataType, object value)
        {
            try
            {
                var write = await WriteCoreAsync(address, dataType, value);
                if (write.IsSuccess)
                    return true;
                return false;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }

        }

        private async Task<OperateResult<object>> ReadCoreAsync(string address, DaDataType dataType, ushort length)
        {
            switch (dataType)
            {
                case DaDataType.BOOL:
                    return (await readWriteNet.ReadBoolAsync(address)).MapTo<OperateResult<object>>();
                case DaDataType.INT16:
                    return (await readWriteNet.ReadInt16Async(address)).MapTo<OperateResult<object>>();
                case DaDataType.UINT16:
                    return (await readWriteNet.ReadUInt16Async(address)).MapTo<OperateResult<object>>();
                case DaDataType.INT32:
                    return (await readWriteNet.ReadInt32Async(address)).MapTo<OperateResult<object>>();
                case DaDataType.UINT32:
                    return (await readWriteNet.ReadUInt32Async(address)).MapTo<OperateResult<object>>();
                case DaDataType.INT64:
                    return (await readWriteNet.ReadInt64Async(address)).MapTo<OperateResult<object>>();
                case DaDataType.UINT64:
                    return (await readWriteNet.ReadUInt64Async(address)).MapTo<OperateResult<object>>();
                case DaDataType.FLOAT:
                    return (await readWriteNet.ReadFloatAsync(address)).MapTo<OperateResult<object>>();
                case DaDataType.DOUBLE:
                    return (await readWriteNet.ReadDoubleAsync(address)).MapTo<OperateResult<object>>();
                case DaDataType.STRING:
                    return (await readWriteNet.ReadStringAsync(address, length)).MapTo<OperateResult<object>>();
                default:
                    throw new Exception("dataType do not exist");
            }
        }
        private Task<OperateResult> WriteCoreAsync(string address, DaDataType dataType, object value)
        {
            switch (dataType)
            {
                case DaDataType.BOOL:
                    return readWriteNet.WriteAsync(address, (bool)value);
                case DaDataType.INT16:
                    return readWriteNet.WriteAsync(address, (short)value);
                case DaDataType.UINT16:
                    return readWriteNet.WriteAsync(address, (ushort)value);
                case DaDataType.INT32:
                    return readWriteNet.WriteAsync(address, (int)value);
                case DaDataType.UINT32:
                    return readWriteNet.WriteAsync(address, (uint)value);
                case DaDataType.INT64:
                    return readWriteNet.WriteAsync(address, (long)value);
                case DaDataType.UINT64:
                    return readWriteNet.WriteAsync(address, (ulong)value);
                case DaDataType.FLOAT:
                    return readWriteNet.WriteAsync(address, (float)value);
                case DaDataType.DOUBLE:
                    return readWriteNet.WriteAsync(address, (double)value);
                case DaDataType.STRING:
                    return readWriteNet.WriteAsync(address, (string)value);
                default:
                    throw new Exception("dataType do not exist");
            }
        }
    }
}
